<?php
class dbt_couponjsdk extends sing_table{
    
    private $_G;
    private $_mid;
    private $values;
    private $_apps;

    //构造方法
    function __construct(){
        global $_G;
        $this->_G = &$_G;
        $this->_mid = $_GET['mid'];
        $this->GETApiuser();
    }
    
    /**
     * 
     * 代金券
     * appid、mchid、nonce_str不需要填入
     * @param AllCoupon $inputObj
     * @param int $timeOut
     * @throws WxPayException
     * @return 成功时返回，其他抛异常
     */
    public function sendCoupon($arr=array(),$timeOut = 6) {
        
        if(!$arr){
            return array();
        }else{
            $this->values = $arr;
        }
        
        //include libfile('wxpay/api','class');
        
        $url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/send_coupon";
        
        $this->SetAppid($this->getAPPID()); //公众账号ID
        $this->SetMch_id($this->getMCHID()); //商户号
        $this->SetNonce_str($this->getNonceStr()); //随机字符串
        $this->SetPartner_trade_no($arr['thisId']); // 商户单据号
        
        unset($this->values['thisId']);
        //var_dump($this->values);

        $this->SetSign(); //签名
        
        //var_dump($this->values);
        $xml = $this->ToXml();

        $startTimeStamp = time(); //请求开始时间
        
        var_dump($xml);
        $response = $this->postXmlCurl($xml, $url, true, $timeOut);
        var_dump($xml);
        $result = $this->Init($response);
        $endTimeStamp = time(); //上报请求花费时间
        
        $result['mid'] = $this->_mid;
        $result['startTimeStamp'] = $startTimeStamp;
        $result['endTimeStamp'] = $endTimeStamp;

        echo 'result：\n';
        var_dump($result);
        return $result;
    }
    
     /**
     * 以post方式提交xml到对应的接口url
     * 
     * @param string $xml  需要post的xml数据
     * @param string $url  url
     * @param bool $useCert 是否需要证书，默认不需要
     * @param int $second   url执行超时时间，默认30s
     * @throws WxPayException
     */
    private function postXmlCurl($xml, $url, $useCert = false, $second = 30) {
        
        $ch = curl_init();
        //设置超时
        curl_setopt($ch, CURLOPT_TIMEOUT, $second);

        //如果有配置代理这里就设置代理
        if (CURL_PROXY_HOST != "0.0.0.0" && CURL_PROXY_PORT != 0) {
            curl_setopt($ch, CURLOPT_PROXY, CURL_PROXY_HOST);
            curl_setopt($ch, CURLOPT_PROXYPORT, CURL_PROXY_PORT);
        }
        echo $url;
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, TRUE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); //严格校验
        //设置header
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        //要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);


        echo 'APICLIENTCERT:'.APICLIENTCERT;
        echo '<br>APICLIENTKEY:'.APICLIENTKEY;

        if ($useCert == true) {
            //设置证书
            //使用证书：cert 与 key 分别属于两个.pem文件
            curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');

            curl_setopt($ch, CURLOPT_SSLCERT, APICLIENTCERT);
            curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
            curl_setopt($ch, CURLOPT_SSLKEY, APICLIENTKEY);
        }
        //post提交方式
        curl_setopt($ch, CURLOPT_POST, TRUE);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        //运行curl
        $data = curl_exec($ch);



        //返回结果
        if ($data) {
            curl_close($ch);
            return $data;
        } else {
            $error = curl_errno($ch);
            curl_close($ch);
            //throw new WxPayException("curl出错，错误码:$error");
        }
    }
    
    /**
     * 将xml转为array
     * @param string $xml
     * @throws WxPayException
     */
    public function Init($xml) {
        
        $this->FromXml($xml);
        //echo 'values：<br>';
        //var_dump($this->values);
        //fix bug 2015-06-29
        if ($this->values['return_code'] != 'SUCCESS') {
            return $this->GetValues();
        }
        //$this->CheckSign();

        return $this->GetValues();
    }
    
    /**
     * 
     * 检测签名
     */
    public function CheckSign() {
        //fix异常
        if (!$this->IsSignSet()) {
            //var_dump($this->values);
            throw new WxPayException("签名错误！1");
        }
        $sign = $this->MakeSign();
        if ($this->GetSign() == $sign) {
            return true;
        }
        throw new WxPayException("签名错误！2");
    }
    
    /**
     * 获取设置的值
     */
    public function GetValues() {
        return $this->values;
    }
    
    /**
     * 设置微信分配的公众账号ID
     * @param string $value 
     * */
    public function SetAppid($value) {
        $this->values['appid'] = $value;
        //$this->values['appid'] = 'wx1b468c340acd675a';
    }
    
     /**
     * 设置微信支付分配的商户号
     * @param string $value 
     * */
    public function SetMch_id($value) {
        $this->values['mch_id'] = $value;
        //$this->values['mch_id'] = '1238414102';
    }
    
    public function SetPartner_trade_no($thisId=0){
        $randStr = $thisId?$thisId:rand(1000000,9999999);
        $this->values['partner_trade_no'] = $this->values['mch_id'].date("Ymd").$randStr;  // 商户单据号
    }
    
    /**
     * 设置随机字符串，不长于32位。推荐随机数生成算法
     * @param string $value 
     * */
    public function SetNonce_str($value) {
        $this->values['nonce_str'] = $value;
    }
    
    /**
     * 获取签名，详见签名生成算法的值
     * @return 值
     * */
    public function GetSign() {
        return $this->values['sign'];
    }
    
    public function GETApiuser($thisBool=false){
        
        $thisData = redis2("get","GETApiuser-".$this->_mid);
        $this->_apps = C::t("#shake#shake")->dataTurn($thisData,"get");
        if(empty($this->_apps) || $thisBool){
            DB::SetMainDB(DB_MAIN);
            $this->_apps = DB::fetch_first('select * from %t where mid=%s', array('wx_apiuser', $this->_mid));
            $thisData = C::t("#shake#shake")->dataTurn($this->_apps);
            redis2("set","GETApiuser-".$this->_mid,$thisData);
        }
        
        //var_dump($this->_apps);

        define(APICLIENTCERT , APP_ROOT.$this->_apps['apiclient_cert']);
        define(APICLIENTKEY ,APP_ROOT.$this->_apps['apiclient_key']);
       
    }
    
    /**
     * 判断签名，详见签名生成算法是否存在
     * @return true 或 false
     * */
    public function IsSignSet() {
        return array_key_exists('sign', $this->values);
    }
    
    public function getAPPID() {
         if(!$this->_apps['AppId']){
            $this->GETApiuser(true);
        }
        return $this->_apps['AppId'];
    }
    public function getMCHID() {
        if(!$this->_apps['wxpay_partnerid']){
            $this->GETApiuser(true);
        }
        return $this->_apps['wxpay_partnerid'];
    }
    public function getKEY() {
        if(!$this->_apps['wxpay_paysignkey']){
            $this->GETApiuser(true);
        }
        return $this->_apps['wxpay_paysignkey'];
    }
    /**
     * 
     * 产生随机字符串，不长于32位
     * @param int $length
     * @return 产生的随机字符串
     */
    public function getNonceStr($length = 32) {
        $chars = "abcdefghijklmnopqrstuvwxyz0123456789";
        $str = "";
        for ($i = 0; $i < $length; $i++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }
    /**
     * 
     * 拼接签名字符串
     * @param array $urlObj
     * 
     * @return 返回已经拼接好的字符串
     */
    private function ToUrlParams($urlObj) {
        $buff = "";
        foreach ($urlObj as $k => $v) {
            if ($k != "sign") {
                $buff .= $k . "=" . $v . "&";
            }
        }

        $buff = trim($buff, "&");
        return $buff;
    }
    
    /**
     * 生成签名
     * @return 签名，本函数不覆盖sign成员变量，如要设置签名需要调用SetSign方法赋值
     */
    public function MakeSign() {
        //$tmpData = $this->_values;
	//unset($tmpData['sign']);
        //签名步骤一：按字典序排序参数
        ksort($this->values);
        
        echo "signAll\n";
        var_dump($this->values);
        $string = $this->ToUrlParams($this->values);
        
        $key = $this->getKEY();
        $string = $string . "&key=" .  $key;
        
        echo "string\n";
        var_dump($string);
        //签名步骤三：MD5加密
        $string = md5($string);
        
        //签名步骤四：所有字符转为大写
        $result = strtoupper($string);

        return $result;
    }
    
    /**
     * 设置签名，详见签名生成算法
     * @param string $value 
     * */
    public function SetSign() {
//        $this->signs['appid'] = $this->values['appid'];
//        $this->signs['mch_id'] = $this->values['mch_id'];
//        $this->signs['nonce_str'] = $this->values['nonce_str'];
        $sign = $this->MakeSign();
        $this->values['sign'] = $sign;
        return $sign;
    }
    
    /**
     * 输出xml字符
     * @throws WxPayException
     * */
    public function ToXml() {
        if (!is_array($this->values) || count($this->values) <= 0) {
            throw new WxPayException("数组数据异常！");
        }

        $xml = "<xml>";
        foreach ($this->values as $key => $val) {
            if (is_numeric($val)) {
                $xml.="<" . $key . ">" . $val . "</" . $key . ">";
            } else {
                $xml.="<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
            }
        }
        $xml.="</xml>";
        return $xml;
    }
    
    /**
     * 将xml转为array
     * @param string $xml
     * @throws WxPayException
     */
    public function FromXml($xml) {
        if (!$xml) {
            throw new WxPayException("xml数据异常！");
        }
        //将XML转为array
        //禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        $this->values = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return $this->values;
    }
    
    //=======【基本信息设置】=====================================
    //
	/**
     * TODO: 修改这里配置为您自己申请的商户信息
     * 微信公众号信息配置
     * 
     * APPID：绑定支付的APPID（必须配置，开户邮件中可查看）
     * 
     * MCHID：商户号（必须配置，开户邮件中可查看）
     * 
     * KEY：商户支付密钥，参考开户邮件设置（必须配置，登录商户平台自行设置）
     * 设置地址：https://pay.weixin.qq.com/index.php/account/api_cert
     * 
     * APPSECRET：公众帐号secert（仅JSAPI支付的时候需要配置， 登录公众平台，进入开发者中心可设置），
     * 获取地址：https://mp.weixin.qq.com/advanced/advanced?action=dev&t=advanced/dev&token=2005451881&lang=zh_CN
     * @var string

      const APPID = ;
      const MCHID = ;
      const KEY = 'e10adc3949ba59abbe56e057f20f883e';
      const APPSECRET = '01c6d59a3f9024db6336662ac95c8e74';
     *  */
    //=======【证书路径设置】=====================================
    /**
     * TODO：设置商户证书路径
     * 证书路径,注意应该填写绝对路径（仅退款、撤销订单时需要，可登录商户平台下载，
     * API证书下载地址：https://pay.weixin.qq.com/index.php/account/api_cert，下载之前需要安装商户操作证书）
     * @var path
     */
    const SSLCERT_PATH = APICLIENTCERT;//'../cert/apiclient_cert.pem';
    const SSLKEY_PATH = APICLIENTKEY;//'../cert/apiclient_key.pem';
    //=======【curl代理设置】===================================
    /**
     * TODO：这里设置代理机器，只有需要代理的时候才设置，不需要代理，请设置为0.0.0.0和0
     * 本例程通过curl使用HTTP POST方法，此处可修改代理服务器，
     * 默认CURL_PROXY_HOST=0.0.0.0和CURL_PROXY_PORT=0，此时不开启代理（如有需要才设置）
     * @var unknown_type
     */
    const CURL_PROXY_HOST = "0.0.0.0"; //"10.152.18.220";
    const CURL_PROXY_PORT = 0; //8080;
    //=======【上报信息配置】===================================
    /**
     * TODO：接口调用上报等级，默认紧错误上报（注意：上报超时间为【1s】，上报无论成败【永不抛出异常】，
     * 不会影响接口调用流程），开启上报之后，方便微信监控请求调用的质量，建议至少
     * 开启错误上报。
     * 上报等级，0.关闭上报; 1.仅错误出错上报; 2.全量上报
     * @var int
     */
    const REPORT_LEVENL = 1;
    
}
class WxPayException extends Exception {

    public function errorMessage() {
        return $this->getMessage();
    }

}